package com.tuananh.radnom.protocols.snmp;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
import org.snmp4j.mp.SnmpConstants;

import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
import com.tuananh.radnom.model.iputils.IPRange;
import com.tuananh.radnom.protocols.snmp.SnmpService.MyBroadcastIPListener;
import com.tuananh.radnom.protocolservice.ActiveDiscover;

@XmlDiscriminatorValue("ActiveSnmp")
public class ActiveSnmp extends ActiveDiscover {
	
	// public ArrayList<String> visitedDevices = new ArrayList<String>();
	SnmpInfo getSnmpInfo(String address) {
		SnmpInfo snmpInfo = new SnmpInfo();
		// get the infomation neccessary to query SNMP service for address
		HashMap<String, String> params = null;
		for (IPRange range : this.getRange()) {
			params = range.getParamByIp(address);
		}
		String versionStr = params.get("version") != null ? params
				.get("version") : "2";
		String retriesStr = params.get("retries") != null ? params
				.get("retries") : "2";
		String timeoutStr = params.get("timeout") != null ? params
				.get("timeout") : "2000";
		int retries = Integer.parseInt(retriesStr);
		long timeout = Long.parseLong(timeoutStr);
		int version = Integer.parseInt(versionStr);
		ArrayList<String> securesInfos = new ArrayList<String>();
		switch (version) {
		case SnmpConstants.version1:
		case SnmpConstants.version2c:
			String readCommunity = params.get("read") != null ? params
					.get("read") : "public";
			String writeCommunity = params.get("write") != null ? params
					.get("write") : readCommunity;
			securesInfos.add(readCommunity);
			securesInfos.add(writeCommunity);
			break;
		case SnmpConstants.version3:
			break;
		}
		snmpInfo.setAddress(address);
		snmpInfo.setRetries(retries);
		snmpInfo.setTimeout(timeout);
		snmpInfo.setSecuresInfo(securesInfos);
		snmpInfo.setVersion(version);
		return snmpInfo;
	}

	private ArrayList<String> nextHopDiscovery(
			ArrayList<String> visitedDevices, String... inputAddresses)
			throws IOException {
		if (visitedDevices == null)
			visitedDevices = new ArrayList<String>();
		for (String address : inputAddresses) {
			// add the address of the device to the visited device set
			visitedDevices.add(address);
			// get the infomation neccessary to query SNMP service for address
			SnmpInfo snmpInfo = getSnmpInfo(address);
			// begin discover
			if (SnmpService.check(snmpInfo.version, "udp:" + address + "/161",
					snmpInfo.retries, snmpInfo.timeout, (String[]) snmpInfo
							.getSecuresInfo().toArray()) != 0) {
				// Query 2 entry object ipRouteNextHop and ipRouteType from the
				// snmp agent
				// ipRouteNextHop = ".1.3.6.1.2.1.4.21.1.7.0" , ipRouteType =
				// ".1.3.6.1.2.1.4.21.1.8.0"
				ArrayList<String> ipRouteNextHop = SnmpService.snmpWalk(
						".1.3.6.1.2.1.4.21.1.7", snmpInfo.version, "udp:"
								+ address + "/161", snmpInfo.retries,
						snmpInfo.timeout,
						(String[]) snmpInfo.securesInfo.toArray());
				ArrayList<String> ipRouteType = SnmpService.snmpWalk(
						".1.3.6.1.2.1.4.21.1.8", snmpInfo.version, "udp:"
								+ address + "/161", snmpInfo.retries,
						snmpInfo.timeout,
						(String[]) snmpInfo.securesInfo.toArray());
				// return if nothing found
				if (ipRouteNextHop == null)
					continue;
				for (int i = 0; i < ipRouteNextHop.size(); i++) {
					String nextHop = ipRouteNextHop.get(i);
					// Online accept the route type 4 = "indirect"
					if (ipRouteType.get(i).equals("4")) {
						if (!visitedDevices.contains(nextHop)) {
							nextHopDiscovery(visitedDevices, nextHop);
						}
					}
				}
			}
		}
		return visitedDevices;
	}

	private ArrayList<String> arpCacheDiscovery(
			ArrayList<String> visitedDevices, String... addresses)
			throws IOException {
		if (visitedDevices == null)
			visitedDevices = new ArrayList<String>();
		for (String address : addresses) {
			// add the address of the device to the visited device set
			visitedDevices.add(address);
			// get the infomation neccessary to query SNMP service for address
			SnmpInfo snmpInfo = getSnmpInfo(address);
			// begin discover
			if (SnmpService.check(snmpInfo.version, "udp:" + address + "/161",
					snmpInfo.retries, snmpInfo.timeout, (String[]) snmpInfo
							.getSecuresInfo().toArray()) != 0) {
				// Query 2 entry object ipNetToMediaAddress and ipNetToMediaType
				// from the snmp agent
				// ipNetToMediaAddress = "1.3.6.1.2.1.4.22.1.3.0",
				// ipNetToMediaType="1.3.6.1.2.1.4.22.1.4.0"
				ArrayList<String> ipNetToMediaAddress = SnmpService.snmpWalk(
						".1.3.6.1.2.1.4.22.1.3", snmpInfo.version, "udp:"
								+ address + "/161", snmpInfo.retries,
						snmpInfo.timeout,
						(String[]) snmpInfo.securesInfo.toArray());
				ArrayList<String> ipNetToMediaType = SnmpService.snmpWalk(
						".1.3.6.1.2.1.4.22.1.4", snmpInfo.version, "udp:"
								+ address + "/161", snmpInfo.retries,
						snmpInfo.timeout,
						(String[]) snmpInfo.securesInfo.toArray());
				// return if nothing found
				if (ipNetToMediaAddress == null)
					continue;

				for (int i = 0; i < ipNetToMediaAddress.size(); i++) {
					String nextAdress = ipNetToMediaAddress.get(i);

					if (ipNetToMediaType.get(i).equals("3"))
						if (!visitedDevices.contains(nextAdress)) {
							arpCacheDiscovery(visitedDevices, nextAdress);
						}
				}
			}
		}
		return visitedDevices;
	}
	public boolean isListContain(ArrayList<String> list,String value){
		for (String str:list)
			if (str.equals(value)) return true;
		return false;
	}
	@Override
	public ArrayList<String> broadcastDiscover(ArrayList<String> broadcastIPs) {
		ArrayList<String> result = new ArrayList<String>();
		// TODO Auto-generated method stub
		for (String address : broadcastIPs) {
			SnmpInfo snmpInfo = getSnmpInfo(address);
			MyBroadcastIPListener listener = new MyBroadcastIPListener();
			ArrayList<String> oids = new ArrayList<String>();
			oids.add(".1.3.6.1.2.1.1.1.0");
			ArrayList<String> temp = SnmpService.broadcastGetOIDs(listener, oids, address,
					snmpInfo.version, snmpInfo.retries, snmpInfo.timeout,
					(String[])snmpInfo.securesInfo.toArray());
			for(String str:temp){
				if (!isListContain(result, str))
					result.add(str);
			}
		}
		return result;
	}

	@Override
	public ArrayList<String> discoverNetInt(ArrayList<String> inputIPs) {
		// TODO Auto-generated method stub
		ArrayList<String> results = new ArrayList<String>(), nextHopDiscover, arpDiscover;
		ArrayList<String> visitedDevices = new ArrayList<String>();
		try {
			nextHopDiscover = nextHopDiscovery(visitedDevices,
					(String[]) inputIPs.toArray());
			results.addAll(nextHopDiscover);

			// clear the visited device set
			visitedDevices = new ArrayList<String>();

			arpDiscover = arpCacheDiscovery(visitedDevices,
					(String[]) nextHopDiscover.toArray());
			arpDiscover.removeAll(nextHopDiscover);// remove duplicate addresses
			results.addAll(arpDiscover);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return results;
	}

	@Override
	public HashMap<String, String> discoverType(ArrayList<String> discoveredIPs) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public HashMap<String, String> discoverConnection(ArrayList<String> inputIPs) {
		// TODO Auto-generated method stub
		return null;
	}

}
